'use strict'

const assert = require('assert');
const EventEmitter3 = require('eventemitter3');

const logger = require('./logger')('TorrentManager');
const Torrent = require('./torrent').Torrent;
const Wire = require('./wire').Wire;
const PeerManager = require('./peer-manager').PeerManager;

const HANDSHAKE_TIMEOUT = 25000; // handshake 超时时间(尽量保持与torrent.js相同)


class TorrentManager extends EventEmitter3 {
  constructor(config) {
    super();
    assert('peerId' in config, 'need peerId');
    assert('wsConfig' in config, 'need wsConfig');
    assert('rtcConfig' in config, 'need rtcConfig');

    this.peerId = config.peerId;
    this.isReady = false;
    this.isClosed = false;

    this._torrentDic = {};        // 以infoHash为索引的torrent字典

    // 初始化PeerManager
    this.peerManager = new PeerManager(this.peerId, {
      rtcConfig: config.rtcConfig,
      wsConfig: config.wsConfig,
      autoCloseConncetWhenErrorOccur: true,       // 自动关闭出错链接
    });
    this._addPeerManagerLisener();
  }

  _addPeerManagerLisener() {
    // ready
    this.peerManager.once('ready', () => {
      this.isReady = true;
      this.emit('ready', this.peerId);
    });

    // 另一个客户端请求建立连接(p2p通道还未建立完成).
    this.peerManager.on('connection', peerConn => {
      let timer = null
        , wire = null;

      peerConn.once('open', () => {
        wire = new Wire(peerConn);

        wire.once('handshake', (infoHash, peerId) => {
          clearTimeout(timer);
          const torrent = this._torrentDic[infoHash];
          if (!torrent) {
            logger.warn(`${peerId} Torrent Request for ${infoHash} not find.`);
            wire.close();
            return;
          }
          torrent.addWire(wire);
        });

        // 如果链接长时间没有有效请求则关闭.
        timer = setTimeout(() => {
          logger.warn(`${peerConn.peerId} handshake timeout.`);
          wire.close();
        }, HANDSHAKE_TIMEOUT);
      });
    });

    // 出现错误
    this.peerManager.once('error', err => {
      this.emit('error', err);
    });
  }

  createPeerConn(peerId) {
    if (!this.isReady) {
      throw new Error('not ready or has been closed.');
    }
    return this.peerManager.connect(peerId, {});
  }

  addTorrent(torrent) {
    assert(torrent instanceof Torrent);
    if (torrent.infoHash in this._torrentDic) {
      throw new Error(`${torrent.fileName} has already in downloading.`);
    }

    if (!this.isReady) {
      throw new Error('not ready yet!');
    }

    if (torrent.isClosed) {
      throw new Error(`${torrent.infoHash} has closed.`);
    }

    this._torrentDic[torrent.infoHash] = torrent;
    torrent.once('close', () => {
      delete this._torrentDic[torrent.infoHash];
    });
    torrent.setTorrentManager(this);
  }

  close() {
    if (this.isClosed) {
      return;
    }
    this.isReady = false;
    this.isClosed = true;

    // 关闭所有torrent
    for (let infoHash in this._torrentDic) {
      this._torrentDic[infoHash].close();
    }

    // 关闭peer manager
    this.peerManager.destroy();
    this.emit('close');
  }
}

module.exports.TorrentManager = TorrentManager;
